import formencode
from sqlalchemy import orm
from sqlalchemy.ext.associationproxy import association_proxy

from cibynet.model.m_rooms_users import RoomUser
from cibynet.model import tables as t
from cibynet.model.utils import UnicodeProperty
from cibynet.model.utils import GetAllObjects, GetOneObject


class Room(object):
  """
  Attributes:
    * label
    * building
  
  Properties:
    * user
    * users
    * userassociations
  """
  
  users = association_proxy('userassociations', 'user',
                            creator=lambda u, **kw:RoomUser(user=u, **kw))
  
  def _get_userassoc(self):
    """Returns the last (chronogical) user association of the room.
    """
    return orm.object_session(self).query(RoomUser).filter_by(room_id=self.id).\
    order_by(t.rooms_users.c.from_date.desc()).first()
  
  def _get_user(self):
    """Returns the last user of the room if he is still living in.
    """
    assoc = self._get_userassoc()
    if assoc and not assoc.to_date:
      return assoc.user
    return None

  def _set_user(self, user, *args):
    """Set the actual user of the room as the given one, if not already set.
    """
    if self.user is user:
      return
    if not user.room is None:
      del user.room
    if not self.user is None:
      del self.user
    self.users.insert(0, user)

  def _del_user(self):
    """The user leaves the room.
    """
    self._get_userassoc().stop()
    
  user = property(_get_user, _set_user, _del_user)
  label = UnicodeProperty("_label")
  
  def __init__(self, building, label):
    """
    Arguments:
      - building  (Building): the building of the room
      - label  (str): the label of the room
    """
    self.building = building
    self.label = label
    
  def __repr__(self):
    #return "Room <%r, %r>" % (self.label, self.building)
    return self.label
    
    
class RoomIDValidator(formencode.validators.FancyValidator):
  """Validate and return a well formated room id.
  """
  def to_python(self, value, c):
    """Returns room object.
    """
    if len(value) == 0:
      if self.not_empty:
        raise formencode.validators.Invalid('Please select a room', value, c)
      return None
    room = GetOneObject(Room, id=int(value))
    if room is None:
      raise formencode.validators.Invalid('This room is not known...', value, c)
    return room
